﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using BCCommon;
using BCCommon.Enums;
using BCCommon.Exceptions;
using BCCommon.Extensions;
using BCDto.UploadHandleComponent;
using BCService.Common.OSS;
using BCService.Common.UploadFileRecord;
using BCService.FileManagement;
using BCService.UploadHandleComponent;
using BCService.Utils;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860

namespace BCTMSRESTful.Controllers.Management.System.Upload
{
    /// <summary>
    /// 上传控制器
    /// </summary>
    [Route("api/v1/sim/[controller]")]
    public class UploadController : ManagementBaseController
    {
        private readonly IConfiguration configuration;
        private readonly IUploadFileInfoHandleService uploadFileInfoHandleService;
        private readonly IUploadHandleService uploadHandleService;
        private readonly IUploadFileRecordService uploadFileRecordService;
        private readonly IOSSObject ossObject;

        public UploadController(IConfiguration configuration,
            IUploadFileInfoHandleService uploadFileInfoHandleService,
            IUploadHandleService uploadHandleService,
            IUploadFileRecordService uploadFileRecordService,
            IOSSObject ossObject)
        {
            this.configuration = configuration;
            this.uploadFileInfoHandleService = uploadFileInfoHandleService;
            this.uploadHandleService = uploadHandleService;
            this.uploadFileRecordService = uploadFileRecordService;
            this.ossObject = ossObject;
        }

        /// <summary>
        /// RemarkType解释说明
        /// </summary>
        /// <returns>返回RemarkType的枚举解释说明</returns>
        [HttpGet("[action]")]
        [ProducesResponseType(typeof(List<string>), 200)]
        public IActionResult RemarkTypeDescription()
        {
            List<string> list = new List<string>();
            foreach (object o in Enum.GetValues(typeof(UploadFileRemarkType)))
            {
                UploadFileRemarkType remarkType = ((UploadFileRemarkType)(int)o);
                list.Add(string.Format("{0}:{1}  {2}", o, Enum.Format(typeof(UploadFileRemarkType), o, "D"), remarkType.GetDescriptionString()));
            }
            //list.Add("如果存在RemarkJSON字段，则必须是JSON格式");
            return this.Ok(list);
        }


        /// <summary>
        /// 获取服务器已上传文件。
        /// </summary>
        /// <response code="200">服务器文件。</response>
        /// <response code="401">请求需要认证或者认证信息已过期。</response>
        /// <response code="403">无权访问指定的功能。</response>
        /// <returns></returns>
        [HttpGet]
        [ProducesResponseType(typeof(UploadResponseDto), 200)]
        [AllowAnonymous]
        public IActionResult Get()
        {
            string action = Request.Query["action"];
            int index, size;
            try
            {
                index = string.IsNullOrWhiteSpace(Request.Query["start"]) ? 0 : Convert.ToInt32(Request.Query["start"]);
                size = string.IsNullOrWhiteSpace(Request.Query["size"]) ? 99999 : Convert.ToInt32(Request.Query["size"]);
            }
            catch (FormatException)
            {
                return Ok(new
                {
                    state = "参数不正确"
                });
            }
            if (action == "config")
            {
                FileInfo fileInfo = new FileInfo(Path.Combine(Directory.GetCurrentDirectory(), "UEditorConfig.json"));

                return Content(string.Format("{0}({1});", Request.Query["callback"], fileInfo.OpenText().ReadToEnd(), "application/javascript"));

            }
            if (action == "listimage" || action == "listfile")
            {
                string fileType = action == "listimage" ? "image" : "office";
                var list = this.uploadFileRecordService.GetUploadFileRecords(fileType, index, size, out int total);
                UploadUEditorResponseDto uploadUEditorResponseDto = new UploadUEditorResponseDto()
                {
                    State = "SUCCESS",
                    Start = index.ToString(),
                    Size = size.ToString(),
                    Total = total.ToString(),
                    List = list.Select(p => p.WebsiteFullName).TransformTo<UEditorFileUrl>(onAfter: (url, file) =>
                    {
                        file.Url = url;
                    }).ToArray()
                };
                string result = JsonConvert.SerializeObject(uploadUEditorResponseDto, new JsonSerializerSettings()
                {
                    ContractResolver = new DefaultContractResolver
                    {
                        NamingStrategy = new SnakeCaseNamingStrategy
                        {
                            ProcessDictionaryKeys = false
                        }
                    }
                });
                if (!string.IsNullOrWhiteSpace(Request.Query["callback"]))
                {
                    return Content(string.Format("{0}({1});", Request.Query["callback"], result), "application/javascript");
                }
                else
                {
                    return Content(result, "text/plain");
                }
            }
            return Ok();
        }

        /// <summary>
        /// 上传图片。
        /// </summary>
        /// <param name="from">上传文件信息</param>
        /// <response code="200">上传地址。</response>
        /// <response code="401">请求需要认证或者认证信息已过期。</response>
        /// <response code="403">无权访问指定的功能。</response>
        /// <returns></returns>
        [HttpPost]
        [ProducesResponseType(typeof(UploadResponseDto), 200)]
        [AllowAnonymous]
        public IActionResult Upload([FromForm] IFormCollection from)
        {
            var data = from.Files["data"];
            string usage = string.IsNullOrWhiteSpace(from["usage"]) ? Request.Query["usage"] : from["usage"];
            UploadFileDto uploadFileDto = uploadFileInfoHandleService.Init(from.Files[0].FileName, usage, ".jpg");

            //if (!Directory.Exists(uploadFileDto.DirectoryPath))
            //{
            //    Directory.CreateDirectory(uploadFileDto.DirectoryPath);
            //}

            if (data == null || data.Length <= 0)
            {
                return BadRequest("文件错误！");
            }
            if (string.IsNullOrWhiteSpace(Request.Query["remark"]))
            {
                if (from.Any(p => p.Key.Equals("remark")))
                {
                    if (Enum.TryParse(typeof(UploadFileRemarkType), from["remark"], out object value))
                    {
                        uploadFileDto.RemarkType = (UploadFileRemarkType)value;
                        if (from.Any(p => p.Key.Equals("remarkJSON")))
                        {
                            uploadFileDto.Remarks = JsonConvert.DeserializeObject<Dictionary<string, object>>(from["remarkJSON"]);
                        }
                    }
                    else
                    {
                        throw new ArgumentException("备注服务类型不正确。");
                    }
                }
            }
            else
            {
                if (Enum.TryParse(typeof(UploadFileRemarkType), Request.Query["remark"], out object value))
                {
                    uploadFileDto.RemarkType = (UploadFileRemarkType)value;
                    if (!string.IsNullOrWhiteSpace(Request.Query["remarkJSON"]))
                    {
                        uploadFileDto.Remarks = JsonConvert.DeserializeObject<Dictionary<string, object>>(Request.Query["remarkJSON"]);
                    }
                }
                else
                {
                    throw new ArgumentException("备注服务类型不正确。");
                }

            }
            uploadFileInfoHandleService.ChangeFolderName(uploadFileDto);
            uploadFileDto.ContentLength = data.Length;
            uploadHandleService.CheckData(uploadFileDto);

            using (FileStream fs = new FileStream(uploadFileDto.FilePath, FileMode.Create))
            {
                data.CopyTo(fs);
                Stream sm = data.OpenReadStream();
                var br = new BinaryReader(sm);
                if (uploadFileDto.FolderName == "image")
                {
                    CheckFileSecurity(br);
                }
                byte[] bytes = br.ReadBytes((int)sm.Length);
                uploadFileDto.Content = bytes;
                fs.Flush();
            }

            UploadResponseDto uploadResponseDto = uploadHandleService.UploadHandle(uploadFileDto);
            uploadResponseDto.Src = uploadFileDto.Src;
            uploadResponseDto.Host = this.configuration["CurrentlyImageHost"];
            if (uploadResponseDto.Src.IndexOf("/") == 0 && uploadResponseDto.Host.Substring(0, uploadResponseDto.Host.Length - 1) == "/")
            {
                uploadResponseDto.Host = uploadResponseDto.Host.TrimEnd('/');
            }
#if !DEBUG
            if (bool.TryParse(configuration["AliyunOSSAPI:Enabled"], out bool enabledOSS))
            {
                if (enabledOSS)
                {
                    string bucketName = configuration["AliyunOSSAPI:Buckets:0:name"];
                    string localFileName = Path.Combine(uploadFileDto.DirectoryPath, uploadFileDto.FileName + uploadFileDto.FileSuffix);
                    string smallLocalFileName = Path.Combine(uploadFileDto.DirectoryPath, uploadFileDto.FileName + "_50x50" + uploadFileDto.FileSuffix);
                    string objectName = uploadFileDto.Src;
                    string smallObjectName = uploadFileDto.Src.Replace(uploadFileDto.FileName, uploadFileDto.FileName + "_50x50" + uploadFileDto.FileSuffix);
                    if (!ossObject.DoesObjectExist(bucketName, objectName))
                    {
                        if (!ossObject.PutObjectCallback(bucketName, objectName, localFileName, uploadFileDto.FileName, out bool fileExists))
                        {
                            uploadFileRecordService.Delete(uploadFileDto.FileName);
                            throw new ArgumentException("原图上传失败");
                        }

                        if (!fileExists)
                        {
                            uploadFileRecordService.Delete(uploadFileDto.FileName);
                        }

                        if (!ossObject.PutObjectCallback(bucketName, smallObjectName, smallLocalFileName, uploadFileDto.FileName, out bool smallFileExists))
                        {
                            uploadFileRecordService.Delete(uploadFileDto.FileName);
                            throw new ArgumentException("缩略图上传失败");
                        }
                    }
                }
            }
#endif
            if (!string.IsNullOrWhiteSpace(Request.Query["usage"]))
            {
                var file = from.Files[0];
                var result = new UploadUEditorResponseDto
                {
                    State = "SUCCESS",
                    Url = uploadResponseDto.Host + uploadResponseDto.Src,
                    Title = file.FileName,
                    Original = file.FileName,
                    Type = file.ContentType,
                    Size = file.Length.ToString()
                };
                if (string.IsNullOrWhiteSpace(Request.Query["callback"]))
                {
                    string json = JsonConvert.SerializeObject(result, new JsonSerializerSettings()
                    {
                        ContractResolver = new DefaultContractResolver
                        {
                            NamingStrategy = new SnakeCaseNamingStrategy
                            {
                                ProcessDictionaryKeys = false
                            }
                        }
                    });
                    return Content(json, "text/plain");
                }
                else
                {
                    return Ok(result);
                }
            }
            return Ok(uploadResponseDto);
        }
        /// <summary>
        /// 文件安全检查
        /// </summary>
        /// <param name="br">二进制字节流</param>
        private void CheckFileSecurity(BinaryReader br)
        {
            string fileClass;
            byte buffer;
            byte[] b = new byte[2];
            buffer = br.ReadByte();
            b[0] = buffer;
            fileClass = buffer.ToString();
            buffer = br.ReadByte();
            b[1] = buffer;
            fileClass += buffer.ToString();
            Dictionary<string, string> securityCode = new Dictionary<string, string>();
            //7790是Exe,8297是rar
            securityCode.Add("255216", "jpg");
            securityCode.Add("7173", "gif");
            securityCode.Add("6677", "BMP");
            securityCode.Add("13780", "PNG");
            if (!securityCode.Any(p => p.Key.Equals(fileClass)))
            {
                throw new UploadFileException("此文件不允许上传。");
            }
        }

        private void Log(IFormCollection from)
        {
            DirectoryInfo directoryInfo = new DirectoryInfo(Directory.GetCurrentDirectory());
            string path = Path.Combine(directoryInfo.FullName, "uploadRecord", DateTime.Now.ToString("yyyy-MM-dd"));
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }
            FileInfo fileInfo = new FileInfo(Path.Combine(path, "uploadRecord.txt").ToString());

            if (!fileInfo.Exists)
            {
                using (StreamWriter sw = fileInfo.CreateText())
                {
                    sw.WriteLine("*****************************************");
                    sw.WriteLine("当前时间：{0}", DateTime.Now.ToString());
                    foreach (var item in from)
                    {
                        sw.WriteLine("{0}:{1}", item.Key, item.Value);
                    }

                }
            }
            else
            {
                using (StreamWriter sw = fileInfo.AppendText())
                {
                    sw.WriteLine("*****************************************");
                    sw.WriteLine("当前时间：{0}", DateTime.Now.ToString());
                    foreach (var item in from)
                    {
                        sw.WriteLine("{0}:{1}", item.Key, item.Value);
                    }
                }
            }
        }
    }
}
